﻿/*
 * This source file is part of the bstring string library.  This code was
 * written by Paul Hsieh in 2002-2015, and is covered by the BSD open source
 * license and the GPL. Refer to the accompanying documentation for details
 * on usage and license.
 */

 /*
  * bstrwrap.h
  *
  * This file is the C++ wrapper for the bstring functions.
  */

#ifndef BSTRWRAP_INCLUDE
#define BSTRWRAP_INCLUDE

  /////////////////// Configuration defines //////////////////////////////

  // WATCOM C/C++ has broken STL and std::iostream support.  If you have
  // ported over STLport, then you can #define BSTRLIB_CAN_USE_STL to use
  // the CBStringList class.
#if defined(__WATCOMC__)
#  if !defined (BSTRLIB_CAN_USE_STL) && !defined (BSTRLIB_CANNOT_USE_STL)
#    define BSTRLIB_CANNOT_USE_STL
#  endif
#  if !defined (BSTRLIB_CAN_USE_IOSTREAM) && !defined (BSTRLIB_CANNOT_USE_IOSTREAM)
#    define BSTRLIB_CANNOT_USE_IOSTREAM
#  endif
#endif

// By default it assumed that STL has been installed and works for your
// compiler.  If this is not the case, then #define BSTRLIB_CANNOT_USE_STL
#if !defined (BSTRLIB_CANNOT_USE_STL) && !defined (BSTRLIB_CAN_USE_STL)
#define BSTRLIB_CAN_USE_STL
#endif

// By default it assumed that std::iostream works well with your compiler.  
// If this is not the case, then #define BSTRLIB_CAN_USE_IOSTREAM
#if !defined (BSTRLIB_CANNOT_USE_IOSTREAM) && !defined (BSTRLIB_CAN_USE_IOSTREAM)
#define BSTRLIB_CAN_USE_IOSTREAM
#endif

// By default it is assumed that your compiler can deal with and has enabled
// exception handlling.  If this is not the case then you will need to 
// #define BSTRLIB_DOESNT_THROW_EXCEPTIONS
#if !defined (BSTRLIB_THROWS_EXCEPTIONS) && !defined (BSTRLIB_DOESNT_THROW_EXCEPTIONS)
#define BSTRLIB_THROWS_EXCEPTIONS
#endif

////////////////////////////////////////////////////////////////////////

#include <stdlib.h>
#include <type_traits>
#include "bstrlib.h"
#include "bstrview.h"

#ifdef __cplusplus

#if defined(BSTRLIB_CAN_USE_STL)

#if defined(__WATCOMC__)
#pragma warning 604 10
#pragma warning 595 10
#pragma warning 594 10
#pragma warning 549 10
#endif

#include <vector>
#include <string>

#if defined(__WATCOMC__)
#pragma warning 604 9
#pragma warning 595 9
#pragma warning 594 9
#endif

#endif

namespace Bstrlib
{
#ifdef BSTRLIB_THROWS_EXCEPTIONS
#if defined(BSTRLIB_CAN_USE_STL)
	struct CBStringException : public std::exception
	{
	private:
		std::string msg;

	public:
		CBStringException(const std::string inmsg) : msg(inmsg)
		{
		}

		~CBStringException() throw () override
		{
		}

		const char* what() const throw () override { return msg.c_str(); }
	};
#else
	struct CBStringException {
	private:
		char* msg;
		int needToFree;
	public:
		CBStringException(const char* inmsg) : needToFree(0) {
			if (inmsg) {
				msg = (char*)malloc(1 + strlen(inmsg));
				if (NULL == msg) msg = "Out of memory";
				else {
					strcpy(msg, inmsg);
					needToFree = 1;
				}
			}
			else {
				msg = "NULL exception message";
			}
		}
		virtual ~CBStringException() throw () {
			if (needToFree) {
				free(msg);
				needToFree = 0;
				msg = NULL;
			}
		}
		virtual const char* what() const throw () { return msg; }
	};
#endif
#define bstringThrow(er) {\
	CBStringException bstr__cppwrapper_exception ("CBString::" er "");\
	throw bstr__cppwrapper_exception;\
}
#else
#define bstringThrow(er) {}
#endif

	struct CBString;

#ifdef _MSC_VER
#pragma warning(disable:4512)
#endif

	class CBCharWriteProtected
	{
		friend struct CBString;

	private:
		const struct tagbstring& s;
		unsigned int idx;

		CBCharWriteProtected(const struct tagbstring& c, int i) : s(c), idx(static_cast<unsigned int>(i))
		{
			if (idx >= static_cast<unsigned>(s.slen))
			{
				bstringThrow("character index out of bounds");
			}
		}

	public:
		char operator =(char c)
		{
			if (s.mlen <= 0)
			{
				bstringThrow("Write protection error");
			}
#ifndef BSTRLIB_THROWS_EXCEPTIONS
			if (idx >= static_cast<unsigned>(s.slen)) return '\0';
#endif
			s.data[idx] = static_cast<unsigned char>(c);
			return static_cast<char>(s.data[idx]);
		}

		unsigned char operator =(unsigned char c)
		{
			if (s.mlen <= 0)
			{
				bstringThrow("Write protection error");
			}
#ifndef BSTRLIB_THROWS_EXCEPTIONS
			if (idx >= static_cast<unsigned>(s.slen)) return '\0';
#endif
			s.data[idx] = c;
			return s.data[idx];
		}

		operator unsigned char() const
		{
#ifndef BSTRLIB_THROWS_EXCEPTIONS
			if (idx >= static_cast<unsigned>(s.slen)) return static_cast<unsigned char>('\0');
#endif
			return s.data[idx];
		}
	};

	template <typename T>
	using bstrview_conv = std::enable_if_t<std::is_convertible_v<const T&, CBStringView>, int>;

	template <typename T>
	using bstrview_nchar_conv = std::enable_if_t<std::conjunction_v<std::is_convertible<const T&, CBStringView>,
		std::negation<std::is_convertible<
		const T&, const char*>>>, int>;

	struct CBString : public tagbstring
	{
		// Constructors
		CBString();
		CBString(char c);
		CBString(unsigned char c);
		CBString(const char* s);
		CBString(int len, const char* s);
		CBString(const CBString& b);
		CBString(CBString&& other) noexcept;

		template <typename T, bstrview_nchar_conv<T> = 0>
		explicit CBString(const T& x)
		{
			slen = x.slen;
			mlen = slen + 1;
			data = nullptr;
			if (slen >= 0 && x.data != NULL) data = static_cast<unsigned char*>(malloc(mlen));
			if (!data)
			{
				bstringThrow("Failure in (tagbstring) constructor");
			}
			memcpy(data, x.data, slen);
			data[slen] = '\0';
		}

		CBString(char c, int len);
		CBString(const void* blk, int len);

#if defined(BSTRLIB_CAN_USE_STL)
		CBString(const struct CBStringList& l);
		CBString(const struct CBStringList& l, const CBString& sep);
		CBString(const struct CBStringList& l, char sep);
		CBString(const struct CBStringList& l, unsigned char sep);
#endif

		// Destructor
#if !defined(BSTRLIB_DONT_USE_VIRTUAL_DESTRUCTOR)
		virtual
#endif
			~CBString();

		// = operator
		const CBString& operator =(char c);
		const CBString& operator =(unsigned char c);
		const CBString& operator =(const char* s);
		const CBString& operator =(const CBString& b);
		const CBString& operator=(CBString&& other) noexcept;

		template <typename T, bstrview_nchar_conv<T> = 0>
		const CBString& operator=(const T& x)
		{
			if (mlen <= 0) bstringThrow("Write protection error");
			if (x.slen < 0) bstringThrow("Failure in =(tagbstring) operator, badly formed tagbstring");
			if (x.slen >= mlen) alloc(x.slen);

			slen = x.slen;
			if (!data)
			{
				mlen = slen = 0;
				bstringThrow("Failure in =(tagbstring) operator");
			}
			memcpy(data, x.data, slen);
			data[slen] = '\0';
			return *this;
		}

		// += operator
		const CBString& operator +=(char c);
		const CBString& operator +=(unsigned char c);
		const CBString& operator +=(const char* s);
		const CBString& operator +=(const CBString& b);

		template <typename T, bstrview_nchar_conv<T> = 0>
		const CBString& operator +=(const T& x)
		{
			if (mlen <= 0) bstringThrow("Write protection error");
			if (x.slen < 0) bstringThrow("Failure in +=(tagbstring) operator, badly formed tagbstring");
			alloc(x.slen + slen + 1);

			if (!data)
			{
				mlen = slen = 0;
				bstringThrow("Failure in +=(tagbstring) operator");
			}
			memcpy(data + slen, x.data, x.slen);
			slen += x.slen;
			data[slen] = '\0';
			return *this;
		}

		// *= operator
		const CBString& operator *=(int count)
		{
			this->repeat(count);
			return *this;
		}

		// + operator
		const CBString operator +(char c) const;
		const CBString operator +(unsigned char c) const;
		const CBString operator +(const unsigned char* s) const;
		const CBString operator +(const char* s) const;
		const CBString operator +(const CBString& b) const;

		// * operator
		const CBString operator *(int count) const
		{
			CBString retval(*this);
			retval.repeat(count);
			return retval;
		}

		// Comparison operators
		bool operator ==(const CBString& b) const;
		bool operator ==(const char* s) const;
		bool operator ==(const unsigned char* s) const;
		bool operator !=(const CBString& b) const;
		bool operator !=(const char* s) const;
		bool operator !=(const unsigned char* s) const;
		bool operator <(const CBString& b) const;
		bool operator <(const char* s) const;
		bool operator <(const unsigned char* s) const;
		bool operator <=(const CBString& b) const;
		bool operator <=(const char* s) const;
		bool operator <=(const unsigned char* s) const;
		bool operator >(const CBString& b) const;
		bool operator >(const char* s) const;
		bool operator >(const unsigned char* s) const;
		bool operator >=(const CBString& b) const;
		bool operator >=(const char* s) const;
		bool operator >=(const unsigned char* s) const;

		operator double() const;
		operator float() const;
		operator int() const;
		operator unsigned int() const;
		operator CBStringView() const;

		// Accessors
		int length() const { return slen; }

		unsigned char character(int i) const
		{
			if (static_cast<unsigned>(i) >= static_cast<unsigned>(slen))
			{
#ifdef BSTRLIB_THROWS_EXCEPTIONS
				bstringThrow("character idx out of bounds");
#else
				return '\0';
#endif
			}
			return data[i];
		}

		unsigned char operator [](int i) const { return character(i); }

		CBCharWriteProtected character(int i)
		{
			return CBCharWriteProtected(*this, i);
		}

		CBCharWriteProtected operator [](int i) { return character(i); }

		// Space allocation hint method.
		void alloc(int length);

		// Search methods.
		int caselessEqual(const CBString& b) const;
		int caselessCmp(const CBString& b) const;
		int boyer_moore_find(const char* b, int pos = 0) const;

		template <typename T, bstrview_nchar_conv<T> = 0>
		int boyer_moore_find(const T& b, int pos = 0) const
		{
			const auto size = slen;
			const auto nsize = b.slen;
			auto needed = reinterpret_cast<char*>(b.data);
			if (nsize + pos > size || nsize + pos < pos)
			{
				return BSTR_ERR;
			}

			if (nsize == 0)
			{
				return pos;
			}
			const auto haystack = reinterpret_cast<char*>(data);
			const auto nsize_1 = nsize - 1;
			const auto last = needed[nsize_1];

			size_t skip = 0;

			auto i = haystack + pos;
			const auto end = haystack + size - nsize_1;

			while (i < end)
			{
				while (i[nsize_1] != last)
				{
					if (++i == end)
					{
						return BSTR_ERR;
					}
				}
				for (size_t j = 0;;)
				{
					if (i[j] != needed[j])
					{
						if (skip == 0)
						{
							skip = 1;
							while (skip <= nsize_1 && needed[nsize_1 - skip] != last)
							{
								++skip;
							}
						}
						i += skip;
						break;
					}
					if (++j == nsize)
					{
						return i - haystack;
					}
				}
			}
			return BSTR_ERR;
		}

		int boyer_moore_caselessfind(const char* b, int pos = 0) const;

		template <typename T, bstrview_nchar_conv<T> = 0>
		int boyer_moore_caselessfind(const T& b, int pos = 0) const
		{
			const auto size = slen;
			const auto nsize = b.slen;
			auto needed = reinterpret_cast<char*>(b.data);
			if (nsize + pos > size || nsize + pos < pos)
			{
				return BSTR_ERR;
			}

			if (nsize == 0)
			{
				return pos;
			}
			const auto haystack = data;
			const auto nsize_1 = nsize - 1;
			const auto last = static_cast<unsigned char>(::tolower(needed[nsize_1]));

			size_t skip = 0;

			auto i = haystack + pos;
			const auto end = haystack + size - nsize_1;

			while (i < end)
			{
				while (static_cast<unsigned char>(::tolower(i[nsize_1])) != last)
				{
					if (++i == end)
					{
						return BSTR_ERR;
					}
				}
				for (size_t j = 0;;)
				{
					if (static_cast<unsigned char>(::tolower(i[j])) != static_cast<unsigned char>(::tolower(needed[j])))
					{
						if (skip == 0)
						{
							skip = 1;
							while (skip <= nsize_1 && static_cast<unsigned char>(::tolower(needed[nsize_1 - skip])) !=
								last)
							{
								++skip;
							}
						}
						i += skip;
						break;
					}
					if (++j == nsize)
					{
						return i - haystack;
					}
				}
			}
			return BSTR_ERR;
		}

		int find(const CBString& b, int pos = 0) const;
		int find(const char* b, int pos = 0) const;

		template <typename T, bstrview_nchar_conv<T> = 0>
		int find(const T& b, int pos = 0) const
		{
			return binstr(this, pos, reinterpret_cast<const_bstring>(&b));
		}

		int caselessfind(const CBString& b, int pos = 0) const;
		int caselessfind(const char* b, int pos = 0) const;

		template <typename T, bstrview_nchar_conv<T> = 0>
		int caselessfind(const T& b, int pos = 0) const
		{
			return binstrcaseless(this, pos, reinterpret_cast<const_bstring>(&b));
		}

		int find(char c, int pos = 0) const;
		int reversefind(const CBString& b, int pos) const;
		int reversefind(const char* b, int pos) const;

		template <typename T, bstrview_nchar_conv<T> = 0>
		int reversefind(const T& b, int pos = 0) const
		{
			return binstrr(this, pos, reinterpret_cast<const_bstring>(&b));
		}

		int caselessreversefind(const CBString& b, int pos) const;
		int caselessreversefind(const char* b, int pos) const;

		template <typename T, bstrview_nchar_conv<T> = 0>
		int caselessreversefind(const T& b, int pos = 0) const
		{
			return binstrrcaseless(this, pos, reinterpret_cast<const_bstring>(&b));
		}

		int reversefind(char c, int pos) const;
		int findchr(const CBString& b, int pos = 0) const;
		int findchr(const char* s, int pos = 0) const;

		template <typename T, bstrview_nchar_conv<T> = 0>
		int findchr(const T& b, int pos = 0) const
		{
			return binchr(this, pos, reinterpret_cast<const_bstring>(&b));
		}

		int reversefindchr(const CBString& b, int pos) const;
		int reversefindchr(const char* s, int pos) const;

		template <typename T, bstrview_nchar_conv<T> = 0>
		int reversefindchr(const T& b, int pos = 0) const
		{
			return binchrr(this, pos, reinterpret_cast<const_bstring>(&b));
		}

		int nfindchr(const CBString& b, int pos = 0) const;
		int nfindchr(const char* b, int pos = 0) const;

		template <typename T, bstrview_nchar_conv<T> = 0>
		int nfindchr(const T& b, int pos = 0) const
		{
			return bninchr(this, pos, reinterpret_cast<const_bstring>(&b));
		}

		int nreversefindchr(const CBString& b, int pos) const;
		int nreversefindchr(const char* b, int pos) const;

		template <typename T, bstrview_nchar_conv<T> = 0>
		int nreversefindchr(const T& b, int pos = 0) const
		{
			return bninchrr(this, pos, reinterpret_cast<const_bstring>(&b));
		}

		// Search and substitute methods.
		void findreplace(const CBString& find, const CBString& repl, int pos = 0);
		void findreplace(const CBString& find, const char* repl, int pos = 0);
		void findreplace(const char* find, const CBString& repl, int pos = 0);
		void findreplace(const char* find, const char* repl, int pos = 0);
		void findreplacecaseless(const CBString& find, const CBString& repl, int pos = 0);
		void findreplacecaseless(const CBString& find, const char* repl, int pos = 0);
		void findreplacecaseless(const char* find, const CBString& repl, int pos = 0);
		void findreplacecaseless(const char* find, const char* repl, int pos = 0);

		// Extraction method.
		const CBString midstr(int left, int len) const;

		// Standard manipulation methods.
		void setsubstr(int pos, const CBString& b, unsigned char fill = ' ');
		void setsubstr(int pos, const char* b, unsigned char fill = ' ');
		template <typename T, bstrview_nchar_conv<T> = 0>
		void setsubstr(int pos, const T& b, unsigned char fill = ' ') const
		{
			bsetstr(this, pos, reinterpret_cast<const_bstring>(&b), fill);
		}
		void insert(int pos, const CBString& b, unsigned char fill = ' ');
		void insert(int pos, const char* b, unsigned char fill = ' ');
		template <typename T, bstrview_nchar_conv<T> = 0>
		void insert(int pos, const T& b, unsigned char fill = ' ') const
		{
			binsert(this, pos, reinterpret_cast<const_bstring>(&b), fill);
		}
		void insertchrs(int pos, int len, unsigned char fill = ' ');
		void replace(int pos, int len, const CBString& b, unsigned char fill = ' ');
		void replace(int pos, int len, const char* s, unsigned char fill = ' ');
		template <typename T, bstrview_nchar_conv<T> = 0>
		void replace(int pos, int len, const T& b, unsigned char fill = ' ')
		{
			breplace(this, pos, len, reinterpret_cast<const_bstring>(&b), fill);
		}
		void remove(int pos, int len);
		void trunc(int len);

		// Miscellaneous methods.
		void format(const char* fmt, ...);
		void formata(const char* fmt, ...);
		void fill(int length, unsigned char fill = ' ');
		void repeat(int count);
		void ltrim(const CBString& b = CBString(bsStaticBlkParms(" \t\v\f\r\n")));
		template <typename T, bstrview_nchar_conv<T> = 0>
		void ltrim(const T& b = " \t\v\f\r\n")
		{
			int l = nfindchr(b, 0);
			if (l == BSTR_ERR) l = slen;
			remove(0, l);
		}
		void rtrim(const CBString& b = CBString(bsStaticBlkParms(" \t\v\f\r\n")));
		template <typename T, bstrview_nchar_conv<T> = 0>
		void rtrim(const T& b = " \t\v\f\r\n")
		{
			const int l = nreversefindchr(b, slen - 1);
			slen = l + 1;
			if (mlen > slen) data[slen] = '\0';
		}
		void trim(const CBString& b = CBString(bsStaticBlkParms(" \t\v\f\r\n")))
		{
			rtrim(b);
			ltrim(b);
		}
		template <typename T, bstrview_nchar_conv<T> = 0>
		void trim(const T& b = " \t\v\f\r\n")
		{
			rtrim(b);
			ltrim(b);
		}

		void toupper();
		void tolower();

		// Write protection methods.
		void writeprotect();
		void writeallow();
		bool iswriteprotected() const { return mlen <= 0; }

		// Join methods.
#if defined(BSTRLIB_CAN_USE_STL)
		void join(const struct CBStringList& l);
		void join(const struct CBStringList& l, const CBString& sep);
		void join(const struct CBStringList& l, char sep);
		void join(const struct CBStringList& l, unsigned char sep);
#endif

		// CBStream methods
		int gets(bNgetc getcPtr, void* parm, char terminator = '\n');
		int read(bNread readPtr, void* parm);
	};

	extern const CBString operator +(const char* a, const CBString& b);
	extern const CBString operator +(const unsigned char* a, const CBString& b);
	extern const CBString operator +(char c, const CBString& b);
	extern const CBString operator +(unsigned char c, const CBString& b);

	inline const CBString operator *(int count, const CBString& b)
	{
		CBString retval(b);
		retval.repeat(count);
		return retval;
	}

#if defined(BSTRLIB_CAN_USE_IOSTREAM)
	extern std::ostream& operator <<(std::ostream& sout, CBString b);
	extern std::istream& operator >>(std::istream& sin, CBString& b);
	extern std::istream& getline(std::istream& sin, CBString& b, char terminator = '\n');
#endif

	struct CBStream
	{
		friend struct CBStringList;

	private:
		struct bStream* m_s;

	public:
		CBStream(bNread readPtr, void* parm);
		~CBStream();
		int buffLengthSet(int sz);
		int buffLengthGet();
		int eof() const;

		CBString readLine(char terminator);
		CBString readLine(const CBString& terminator);
		void readLine(CBString& s, char terminator);
		void readLine(CBString& s, const CBString& terminator);
		void readLineAppend(CBString& s, char terminator);
		void readLineAppend(CBString& s, const CBString& terminator);

		CBString read();
		CBString& operator >>(CBString& s);

		CBString read(int n);
		void read(CBString& s);
		void read(CBString& s, int n);
		void readAppend(CBString& s);
		void readAppend(CBString& s, int n);

		void unread(const CBString& s);

		CBStream& operator <<(const CBString& s)
		{
			this->unread(s);
			return *this;
		}

		CBString peek() const;
		void peek(CBString& s) const;
		void peekAppend(CBString& s) const;
	};

#if defined(BSTRLIB_CAN_USE_STL)
	struct CBStringList : public std::vector<CBString>
	{
		// split a string into a vector of strings.
		void split(const CBString& b, unsigned char splitChar);
		void split(const CBString& b, const CBString& s);
		void splitstr(const CBString& b, const CBString& s);
		void split(const CBStream& b, unsigned char splitChar);
		void split(const CBStream& b, const CBString& s);
		void splitstr(const CBStream& b, const CBString& s);
	};
#endif
} // namespace Bstrlib

#if !defined (BSTRLIB_DONT_ASSUME_NAMESPACE)
using namespace Bstrlib;
#endif

#endif
#endif
